#pragma once

#include <string>
#include "json.hpp"

namespace alpha { namespace rpc {

namespace detail {

template <typename Stream, typename T>
void write_json_string(Stream &ss, T const &v)
{
	ss << '"';
	for (typename T::const_iterator i = v.begin(); i != v.end(); ++i)
	{
		switch (*i)
		{
			case '\b':
				ss << "\\b";
				break;
			case '\f':
				ss << "\\f";
				break;
			case '\n':
				ss << "\\n";
				break;
			case '\r':
				ss << "\\r";
				break;
			case '\t':
				ss << "\\t";
				break;
			case '"':
			case '\\':
			case '/':
				ss << '\\';
			default:
				ss << *i;
				break;
		}
	}
	ss << '"';
}

template <typename Stream, typename T>
void write_json(Stream &stream, T const &r, std::string const &spaces)
{
	typedef typename T::null null;
	typedef typename T::boolean boolean;
	typedef typename T::number number;
	typedef typename T::string string;
	typedef typename T::array array;
	typedef typename T::object object;

	if (r.template pointer<null>())
		stream << "null";
	else if (boolean const *v = r.template pointer<boolean>())
		stream << (*v)? "true": "false";
	else if (number const *v = r.template pointer<number>())
		stream << *v;
	else if (string const *v = r.template pointer<string>())
		detail::write_json_string(stream, *v);
	else if (array const *v = r.template pointer<array>())
	{
		stream << '[';
		if (!v->empty())
		{
			typename array::const_iterator i = v->begin();
			stream << *i;
			for (++i; i != v->end(); ++i)
				stream << ',' << *i;
		}
		stream << ']';
	}
	else if (object const *v = r.template pointer<object>())
	{
		stream << '{';
		if (!v->empty())
		{
			std::string s = spaces + "  ";
			typename object::const_iterator i = v->begin();
			stream << std::endl << s;
			detail::write_json_string(stream, i->first);
			stream << ": ";
			write_json(stream, i->second, s);
			for (++i; i != v->end(); ++i)
			{
				stream << ',' << std::endl << s;
				detail::write_json_string(stream, i->first);
				stream << ": ";
				write_json(stream, i->second, s);
			}
		}
		stream << std::endl << spaces << '}';
	}
	else
		stream.setstate(std::ios::failbit);
}

} // namespace detail

template <typename Stream, template <typename> class Tmpl>
Stream & operator <<(Stream &stream, basic_json<Tmpl> const &v)
{
	detail::write_json(stream, v, "");
	return stream;
}

}} // namespace alpha::rpc
